import os
import json
import threading
import time
from datetime import datetime
import tkinter
from tkinter.ttk import Separator   # 分隔控件(Seperator)
import ctypes

from pynput import keyboard, mouse
from pynput.keyboard import Controller as KeyBoardController, KeyCode
from pynput.mouse import Button, Controller as MouseController


# 键盘和鼠标动作模板
def actions_template(action):
    if action == 'keyboard':
        return {
            "name": "keyboard",
            "event": "default",
            "vk": "default",
            "time": 0
        }
    else:
        return {
            "name": "mouse",
            "event": "default",
            "target": "default",
            "action": "default",
            "location": {
                "x": "0",
                "y": "0"
            },
            "time":0
        }  
    

# 倒计时监听，更新UI触发自定义线程对象
class UIUpdateCutDownExecute(threading.Thread):
    def __init__(self, cut_down_time, custom_thread_list):
        super().__init__()
        self.cut_down_time = cut_down_time
        self.custom_thread_list = custom_thread_list

    def run(self):
        while self.cut_down_time > 0:
            for custom_thread in self.custom_thread_list:
                if custom_thread['obj_ui'] is not None:
                    custom_thread['obj_ui']['text'] = str(self.cut_down_time)+' 秒后开始'
                    custom_thread['obj_ui']['state'] = 'disabled'
                    self.cut_down_time = self.cut_down_time - 1
            time.sleep(1)
        else:
            for custom_thread in self.custom_thread_list:
                if custom_thread['obj_ui'] is not None:
                    custom_thread['obj_ui']['text'] = custom_thread['final_text']
                    custom_thread['obj_ui']['state'] = 'disabled'
                if custom_thread['obj_thread'] is not None:
                    custom_thread['obj_thread'].start()
                    time.sleep(1)


# 键盘动作监听
class KeyboardActionListener(threading.Thread):
    def __init__(self, file_name='actions.json'):
        super().__init__()
        self.file_name = file_name
        
    def run(self):
        with open(self.file_name, 'a', encoding='utf-8') as file:
            # 键盘按下监听
            def on_press(key):
                template = actions_template('keyboard')
                template['event'] = 'press'
                template['time'] = time.time()
                try:
                    template['vk'] = key.vk
                except AttributeError:
                    template['vk'] = key.value.vk
                finally:
                    file.writelines(json.dumps(template) + "\n")
                    file.flush()

            # 键盘抬起监听
            def on_release(key):
                if key == keyboard.Key.esc:
                    # 停止监听
                    startListenerBtn['text'] = '开始录制'
                    startListenerBtn['state'] = 'normal'
                    startExecuteBtn['state'] = 'normal'
                    MouseActionListener.esc_key = True
                    keyboardListener.stop()
                    return False
                template = actions_template('keyboard')
                template['event'] = 'release'
                template['time'] = time.time()
                try:
                    template['vk'] = key.vk
                except AttributeError:
                    template['vk'] = key.value.vk
                finally:
                    file.writelines(json.dumps(template) + "\n")
                    file.flush()

            # 键盘监听
            with keyboard.Listener(on_press=on_press, on_release=on_release) as keyboardListener:
                keyboardListener.join()

# 鼠标动作监听
class MouseActionListener(threading.Thread):
    esc_key = False

    def __init__(self, file_name='actions.json'):
        super().__init__()
        self.file_name = file_name

    def close_listener(self, listener):
        if self.esc_key:
            listener.stop()

    def run(self):
        with open(self.file_name, 'a', encoding='utf-8') as file:
            # 鼠标移动事件
            def on_move(x, y):
                template = actions_template('mouse')
                template['event'] = 'move'
                template['location']['x'] = x/ScaleFactor
                template['location']['y'] = y/ScaleFactor
                template['time'] = time.time()
                file.writelines(json.dumps(template) + "\n")
                file.flush()
                self.close_listener(mouseListener)
                # print('鼠标位置：', x, y)
                
            # 鼠标点击事件
            def on_click(x, y, button, pressed):
                template = actions_template('mouse')
                template['event'] = 'click'
                template['target'] = button.name
                template['action'] = pressed
                template['location']['x'] = x/ScaleFactor
                template['location']['y'] = y/ScaleFactor
                template['time'] = time.time()
                file.writelines(json.dumps(template) + "\n")
                file.flush()
                self.close_listener(mouseListener)

            # 鼠标滚动事件
            def on_scroll(x, y, x_axis, y_axis):
                template = actions_template('mouse')
                template['event'] = 'scroll'
                template['location']['x'] = x_axis/ScaleFactor
                template['location']['y'] = y_axis/ScaleFactor
                template['time'] = time.time()
                file.writelines(json.dumps(template) + "\n")
                file.flush()
                self.close_listener(mouseListener)

            with mouse.Listener(on_move=on_move, on_click=on_click, on_scroll=on_scroll) as mouseListener:
                mouseListener.join()

# 键盘和鼠标动作执行
class ActionExecute(threading.Thread):

    def __init__(self, file_name='actions.json', execute_count=0, execute_speed=0):
        super().__init__()
        self.file_name = file_name
        self.execute_count = execute_count
        self.execute_speed = execute_speed/100

    def run(self):
        total_count = self.execute_count
        print(datetime.now(),f'开始执行{self.execute_count}次回放')
        playCountEdit['state'] = 'disabled'
        playSpeedEdit['state'] = 'disabled'
        keyboard_exec = KeyBoardController()
        mouse_exec = MouseController()
        while self.execute_count > 0:
            # 从文件中读取操作记录
            with open(self.file_name, 'r', encoding='utf-8') as file:
                for line in file:
                    action = json.loads(line)
                    # print('action["name"]:', action["name"])
                    if "start record time" in action.keys():
                        start_time = action["start record time"]
                        # print(start_record_time)
                    elif action["name"] == "keyboard":
                        if action['event'] == 'press':
                            keyboard_exec.press(KeyCode.from_vk(action['vk']))
                            sleep_time = action['time']-start_time
                            time.sleep(sleep_time/self.execute_speed)
                            
                            start_time = action['time']
                        
                        elif action['event'] == 'release':
                                keyboard_exec.release(KeyCode.from_vk(action['vk']))
                                sleep_time = action['time']-start_time
                                time.sleep(sleep_time/self.execute_speed)
                                
                                start_time = action['time']
                            
                    elif action['name'] == "mouse":
                        if action['event'] == 'move':
                            # print(action['location']['x'], action['location']['y'])
                            mouse_exec.position = (action['location']['x'], action['location']['y'])
                            sleep_time = action['time']-start_time
                            time.sleep(sleep_time/self.execute_speed)
                            
                            start_time = action['time']
                            
                        elif action['event'] == 'click':
                            if action['action']:
                                if action['target'] == 'left':
                                    mouse_exec.press(Button.left)
                                else:
                                    mouse_exec.press(Button.right)
                            else:
                                if action['target'] == 'left':
                                    mouse_exec.release(Button.left)
                                else:
                                    mouse_exec.release(Button.right)
                            sleep_time = action['time']-start_time
                            time.sleep(sleep_time/self.execute_speed)
                            
                            start_time = action['time']
                            
                        elif action['event'] == 'scroll':
                            mouse_exec.scroll(action['location']['x'], action['location']['y'])
                            sleep_time = action['time']-start_time
                            time.sleep(sleep_time/self.execute_speed)
                            
                            start_time = action['time']

                startExecuteBtn['text'] = '开始回放'
                startExecuteBtn['state'] = 'normal'
            print(datetime.now(), f'第 {total_count-self.execute_count+1} 次回放完成')
            self.execute_count = self.execute_count - 1
            
        startListenerBtn['state'] = 'normal'
        playCountEdit['state'] = 'normal'
        playSpeedEdit['state'] = 'normal'
        print('*'*10, datetime.now(), '全部回放完成'+'*'*10)

def command_adapter(action):    
    if action == 'listener':
        # 初始化文件actions.json
        with open('actions.json', 'w') as f:
            f.writelines('{'+f'"start record time": {time.time()}'+'}\n')
            
        custom_thread_list = [
            {
                'obj_thread': KeyboardActionListener(),
                'obj_ui': startListenerBtn,
                'final_text': '录制中...esc停止录制'
            },
            {
                'obj_thread': MouseActionListener(),
                'obj_ui': None,
                'final_text': None
            }
        ]
        MouseActionListener.esc_key = False
        UIUpdateCutDownExecute(startTime.get(), custom_thread_list).start()
        
        startExecuteBtn['text'] = '开始回放'
        startExecuteBtn['state'] = 'disabled'

    elif action == 'execute':
        if os.path.isfile('actions.json') and os.path.getsize('actions.json'):
            pass
        else:
            print('你还没有录制好')
            startExecuteBtn['text'] = '你还没有录制好，请先录制'
            return
            
        custom_thread_list = [
            {
                'obj_thread': ActionExecute(execute_count=playCount.get(), execute_speed=playSpeed.get()),
                'obj_ui': startExecuteBtn,
                'final_text': '回放中...关闭程序停止回放'
            }
        ]
        UIUpdateCutDownExecute(endTime.get(), custom_thread_list).start()
        startListenerBtn['state'] = 'disabled'
        
            
if __name__ == '__main__':
    #调用api获得当前的缩放因子
    ScaleFactor=ctypes.windll.shcore.GetScaleFactorForDevice(0)
    ScaleFactor = ScaleFactor/100
    # print('屏幕缩放比例:', ScaleFactor)

    # 获取该脚本所在的目录
    script_path = os.path.dirname(os.path.abspath(__file__))
    os.chdir(script_path)
    
    root = tkinter.Tk()
    root.title('按键精灵-(蓝士钦)-修改版')
    root.geometry('200x230+400+100')

    listenerStartLabel = tkinter.Label(root, text='录制倒计时')
    listenerStartLabel.place(x=10, y=10, width=80, height=20)
    startTime = tkinter.IntVar()
    listenerStartEdit = tkinter.Entry(root, textvariable=startTime)
    listenerStartEdit.place(x=100, y=10, width=60, height=20)
    startTime.set(3)

    listenerTipLabel = tkinter.Label(root, text='秒')
    listenerTipLabel.place(x=160, y=10, width=20, height=20)

    listenerTipLabel2 = tkinter.Label(root, text='提示：按Esc停止录制')
    listenerTipLabel2.place(x=45, y=35, width=120, height=20)
    
    startListenerBtn = tkinter.Button(root, text="开始录制", command=lambda: command_adapter('listener'))
    startListenerBtn.place(x=10, y=55, width=180, height=30)

    #分割线
    sep1 = Separator(root,orient='horizontal')
    sep1.pack(padx=1,pady=89,fill='x')
        
    executeEndLabel = tkinter.Label(root, text='回放倒计时')
    executeEndLabel.place(x=10, y=95, width=80, height=20)
    endTime = tkinter.IntVar()
    executeEndEdit = tkinter.Entry(root, textvariable=endTime)
    executeEndEdit.place(x=100, y=95, width=60, height=20)
    endTime.set(2)

    executeTipLabel = tkinter.Label(root, text='秒')
    executeTipLabel.place(x=160, y=95, width=20, height=20)

    playCountLabel = tkinter.Label(root, text='回放次数')
    playCountLabel.place(x=10, y=125, width=80, height=20)
    playCount = tkinter.IntVar()
    playCountEdit = tkinter.Entry(root, textvariable=playCount)
    playCountEdit.place(x=100, y=125, width=60, height=20)
    playCount.set(1)

    playCountTipLabel = tkinter.Label(root, text='次')
    playCountTipLabel.place(x=160, y=125, width=20, height=20)
    
    playSpeedLabel = tkinter.Label(root, text='回放速度')
    playSpeedLabel.place(x=10, y=155, width=80, height=20)
    playSpeed = tkinter.IntVar()
    playSpeedEdit = tkinter.Entry(root, textvariable=playSpeed)
    playSpeedEdit.place(x=100, y=155, width=60, height=20)
    playSpeed.set(100)

    playSpeedTipLabel = tkinter.Label(root, text='%')
    playSpeedTipLabel.place(x=160, y=155, width=20, height=20)

    startExecuteBtn = tkinter.Button(root, text="开始回放", command=lambda: command_adapter('execute'))
    startExecuteBtn.place(x=10, y=185, width=180, height=30)
    root.mainloop()